/**
 * This sketch:
 * - dump codes read from an IR-receiver at RECV_PIN to the serial port.
 * - reads codes from the serial port and sends to SEND_PIN.
 * Depends on http://github.com/shirriff/Arduino-IRremote
 */

#include <IRremote.h>

int RECV_PIN = 11;

IRrecv irrecv(RECV_PIN);

decode_results results;

void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
}

void WriteInt(unsigned int value) {
  Serial.write((byte)((value & 0xff00) >> 8)); 
  Serial.write((byte)(value & 0x00ff));
}

void WriteLong(unsigned long value) {
  WriteInt((unsigned int)((value & 0xffff0000) >> 16));
  WriteInt((unsigned int)(value & 0x0000ffff));
}

void DecodeAndSendToSerial() {
  if (irrecv.decode(&results)) {
    WriteInt(0xfedc);
    Serial.write((byte)(results.rawlen * 2));
    Serial.write((byte)results.decode_type);
    Serial.write((byte)results.bits);
    WriteLong(results.value);
    for (int i = 0; i < results.rawlen; ++i) {
      WriteInt(results.rawbuf[i]);
    }
    irrecv.resume(); // Receive the next value
  }
}

unsigned int ReadInt() {
  unsigned int value = Serial.read();
  return (value << 8) | Serial.read();
}

unsigned long ReadLong() {
  unsigned long value = ReadInt();
  return (value << 16) | ReadInt();
}


#define STATE_START 0
#define STATE_HAS_HEADER 1
#define STATE_DONE 2

#define MAX_RAW_BUFFER 256

struct {
  byte state = STATE_START;
  int raw_length;
  int decoded_type;
  int num_bits;
  unsigned long decoded_value;
  int cur_buf;
  unsigned int raw_buffers[MAX_RAW_BUFFER] = {0};
} cmd_buffer;

IRsend irsend;

void SendCmdBufferToIR() {
  Serial.print("sending ");
  Serial.print(cmd_buffer.decoded_type);
  Serial.print(' ');
  Serial.print(cmd_buffer.num_bits);
  Serial.print(' ');
  Serial.print(cmd_buffer.decoded_value, HEX);
  if (cmd_buffer.decoded_type == SONY) {
    for (int i = 0; i < 3; i++) {
      irsend.sendSony(cmd_buffer.decoded_value,
                      cmd_buffer.num_bits);
      delay(40);
    }
  } else if (cmd_buffer.decoded_type == SAMSUNG) {
    irsend.sendSamsung(cmd_buffer.decoded_value,
                       cmd_buffer.num_bits);
  } else {
    for (int i = 0; i < cmd_buffer.raw_length; ++i)  
      Serial.println(cmd_buffer.raw_buffers[i], HEX);
    for (int i = 0; i < 3; i++) {
      irsend.sendRaw(cmd_buffer.raw_buffers,
                     cmd_buffer.raw_length,
                     38);
    }
  }
}

void ReadCmdBufferFromSerial() {
  if (cmd_buffer.state == STATE_START) {
    if (Serial.available() >= 9) {
      unsigned int header = ReadInt();
      cmd_buffer.raw_length = Serial.read() / 2;
      cmd_buffer.decoded_type = Serial.read();
      cmd_buffer.num_bits = Serial.read();
      cmd_buffer.decoded_value = ReadLong();
      if (header == 0xfedc) {
        cmd_buffer.state = STATE_HAS_HEADER;
        if (cmd_buffer.raw_length == 0)
          cmd_buffer.state = STATE_DONE;
      }
    }
  }
  if (cmd_buffer.state == STATE_HAS_HEADER) {
    while (Serial.available() >= 2) {
      int value = ReadInt();
      cmd_buffer.raw_buffers[cmd_buffer.cur_buf++] =
          value;
      if (cmd_buffer.cur_buf >= cmd_buffer.raw_length) {
       cmd_buffer.state = STATE_DONE;
       break;
      } else if (cmd_buffer.cur_buf > MAX_RAW_BUFFER) {
        cmd_buffer.cur_buf = 0;
        cmd_buffer.state = STATE_START;
        break;
      }
    }
  }
  if (cmd_buffer.state == STATE_DONE) {
    SendCmdBufferToIR();
    memset(&cmd_buffer, 0, sizeof(cmd_buffer));
  }
}

void loop() {
  // Try to decode any incoming beam and send it over
  // serial so that the server can learn the code.
  DecodeAndSendToSerial();
  // Read the command buffer the server sent in the serial,
  // and send it to the IR.
  ReadCmdBufferFromSerial();
}

